
#include "stdafx.h"
#include "TimelineView.h"
#include "Timeline.h"
#include "Global.h"
#include <math.h>


#ifdef _DEBUG
#define new DEBUG_NEW
#endif

#define LEFT_SPACE 20
#define TOP_SPACE 30
#define ROW_HEIGHT 15
#define FRAME_X_SPACE 8
#define MIN_FRAME_HEIGHT 6
#define MAX_FRAME_HEIGHT 10
#define FONT_WIDTH 40
#define FONT_HEIGHT 15
#define GF_Y_SPACE 15

#define FLAG_HEIGHT 7



// CFlag
CFlag::CFlag()
: m_eStatus(eUnSelect)
{
}

CFlag::~CFlag()
{
}

void CFlag::Draw(CDC* pDC, int iScrollX)
{
	m_iScrollX = iScrollX;

	POINT pt2 = m_oPosition;
	pt2.x -= iScrollX;
	POINT pt = pt2;
	pt.y -= FLAG_HEIGHT;

	if (m_eStatus == ePreSelecting || m_eStatus == eSelecting)
	{
		CRect rect;
		rect.SetRect(pt.x, pt.y - FLAG_HEIGHT, pt.x + FLAG_HEIGHT, pt.y);
		pDC->FillRect(rect, &CBrush(RGB(255, 0, 0)));
	}
	else
	{
		POINT points[4];
		points[0] = pt;
		points[1].x = pt.x;
		points[1].y = pt.y - FLAG_HEIGHT;
		points[2].x = pt.x + FLAG_HEIGHT;
		points[2].y = pt.y - FLAG_HEIGHT;
		points[3].x = pt.x + FLAG_HEIGHT;
		points[3].y = pt.y;

		BOOL bOk = pDC->Polygon(points, 4);
	}

	pDC->MoveTo(pt2);
	pDC->LineTo(pt);
}

CRect CFlag::PreSelect()
{
	m_eStatus = ePreSelecting;

	POINT pt2 = m_oPosition;
	pt2.x -= m_iScrollX;
	POINT pt = pt2;
	pt.y -= FLAG_HEIGHT;

	CRect rect;
	rect.SetRect(pt.x, pt.y - FLAG_HEIGHT, pt.x + FLAG_HEIGHT, pt.y);

	return rect;
}

void CFlag::UnSelect()
{
	m_eStatus = eUnSelect;
}


// CFlagMap
CFlagMap::CFlagMap()
{
}

CFlagMap::~CFlagMap()
{
}

void CFlagMap::Add(CFlag* pFlag)
{
	POINT pt = pFlag->GetPosition();
	CString key;
	key.Format(_T("X%d_Y%d"), pt.x, pt.y);

	FlagMap::iterator it = m_oFlagMap.find(key);
	if (it == m_oFlagMap.end())
	{
		m_oFlagMap.insert(FlagPair(key, pFlag));
	}
	else
	{
		delete pFlag;
		pFlag = NULL;
	}
}

void CFlagMap::DeleteAll()
{
	for (FlagMap::iterator it = m_oFlagMap.begin();
		it != m_oFlagMap.end(); it ++)
	{
		delete it->second;
		it->second = NULL;
	}
	m_oFlagMap.clear();
}

void CFlagMap::DeleteAllSelection()
{
	FlagMap::iterator it = m_oFlagMap.begin();
	while(it != m_oFlagMap.end())
	{
		CFlag* pFlag = it->second;
		if (pFlag->GetStatus() == CFlag::eSelecting)
		{
			FlagMap::iterator it2 = it;
			it ++;

			pFlag->UnSelect();
			delete pFlag;
			pFlag = NULL;
			m_oFlagMap.erase(it2);
		}
		else
		{
			it ++;
		}
	}
}

void CFlagMap::DrawAll(CDC* pDC)
{
	for (FlagMap::iterator it = m_oFlagMap.begin();
		it != m_oFlagMap.end(); it ++)
	{
		CFlag* pFlag = it->second;
		pFlag->Draw(pDC, m_iScroll);
	}
}

CFlag* CFlagMap::Find(POINT pt)
{
	CString key;
	key.Format(_T("X%d_Y%d"), pt.x, pt.y);

	return Find(key);
}

CFlag* CFlagMap::Find(CString& strID)
{
	FlagMap::iterator it = m_oFlagMap.find(strID);
	if (it != m_oFlagMap.end())
		return it->second;
	else
		return NULL;
}



// CSelectingRect
CSelectingRect::CSelectingRect()
: m_bExisted(false),
  m_eStatus(eNone),
  m_iScroll(0)
{
}

CSelectingRect::~CSelectingRect()
{
}

void CSelectingRect::SetRect(int x1, int y1, int x2, int y2)
{
	m_bExisted = true;
	m_rect.SetRect(x1, y1, x2, y2);
}

CRect CSelectingRect::GetRect()
{
	CRect tempRect;
	tempRect = m_rect;
	tempRect.left = tempRect.left - m_iScroll;
	tempRect.right = tempRect.right - m_iScroll;

	return tempRect;
}

void CSelectingRect::Draw(CDC* pDC)
{
	CRect tempRect;
	tempRect = m_rect;
	tempRect.left = tempRect.left - m_iScroll;
	tempRect.right = tempRect.right - m_iScroll;
	pDC->Rectangle(tempRect);
	if (m_eStatus == ePreSelecting || m_eStatus == eSelecting)
		pDC->FillRect(tempRect, &CBrush(RGB(255, 0, 0)));
	else
		pDC->FillRect(tempRect, &CBrush(RGB(0, 0, 0)));

	POINT cenPt;
	cenPt = tempRect.CenterPoint();

	int width = tempRect.Width();
	pDC->MoveTo(cenPt.x, cenPt.y - width / 2);
	pDC->LineTo(cenPt.x, m_maxRect.top);
	pDC->MoveTo(cenPt.x, cenPt.y + width / 2);
	pDC->LineTo(cenPt.x, m_maxRect.bottom);
}

bool CSelectingRect::IsSelecting(POINT pt)
{
	CRect tempRect;
	tempRect = m_rect;
	tempRect.left = WToU(tempRect.left);
	tempRect.right = WToU(tempRect.right);
	POINT cenPt = tempRect.CenterPoint();

	double len = sqrt((double)((cenPt.x - pt.x) * (cenPt.x - pt.x) + (cenPt.y - pt.y) * (cenPt.y - pt.y)));
	double radius = sqrt(2.0) * 0.5 * tempRect.Width();
	if (len > radius)
	{
		return false;
	}
	else
	{
		return true;
	}
}

void CSelectingRect::Move(int DeltaX)
{
	m_rect.left += DeltaX;
	m_rect.right += DeltaX;
}

void CSelectingRect::MoveTo(int x)
{
	POINT pt = m_rect.CenterPoint();
	Move(x - pt.x);
}

int CSelectingRect::WToU(int x)
{
	return x - 2*m_iScroll;
}


// CGraduation
CGraduation::CGraduation()
: m_UnitSpace(0),
  m_iFrameCount(0),
  m_iScroll(0)
{
}

CGraduation::~CGraduation()
{
}

void CGraduation::Draw(CDC* pDC)
{
	// draw graduation
	POINT gPt;
	gPt = m_oGraduationStartPt;
	gPt.x = WToU(gPt.x);
	for (long lIndex = 0; lIndex <= m_iFrameCount; lIndex ++)
	{
		POINT pt2 = gPt;
		if (lIndex % 6 == 0)
		{
			pt2.y -= MAX_FRAME_HEIGHT;

			CRect fontRect;
			fontRect.SetRect(pt2.x - (FONT_WIDTH / 2), pt2.y - FONT_HEIGHT, pt2.x + (FONT_WIDTH / 2), pt2.y);

			CString strIndex;
			strIndex.Format(_T("%ld"), lIndex);
			pDC->DrawText(strIndex, fontRect,DT_CENTER | DT_VCENTER);
		}
		else
		{
			pt2.y -= MIN_FRAME_HEIGHT;
		}

		pDC->MoveTo(gPt);
		pDC->LineTo(pt2);

		gPt.x += FRAME_X_SPACE;
	}
}

int CGraduation::WToU(int x)
{
	return x - m_iScroll;
}

int CGraduation::OnSelect(int sourceX)
{
	POINT Pt2;
	Pt2 = m_oGraduationStartPt;

	int delta = abs(sourceX - Pt2.x);
	int iNum = (int)(delta / FRAME_X_SPACE);
	int x2 = iNum * FRAME_X_SPACE + Pt2.x;
	int x3 = x2 + FRAME_X_SPACE;

	if (abs(x2 - sourceX) < abs(x3 - sourceX))
	{
		return x2;
	}
	else
	{
		return x3;
	}
}

int CGraduation::GetIndex(int x)
{
	POINT gPt;
	gPt = m_oGraduationStartPt;

	int Delta = x - gPt.x;;
	int iNum = (int)(Delta / FRAME_X_SPACE);
	return iNum;
}



// CUILine
CUILine::CUILine()
{
}

CUILine::~CUILine()
{
}

void CUILine::Draw(CDC* pDC)
{
	pDC->MoveTo(m_startPt);
	pDC->LineTo(m_endPt);
}




// CTimelineView
IMPLEMENT_DYNCREATE(CTimelineView, CScrollView)

BEGIN_MESSAGE_MAP(CTimelineView, CScrollView)
	ON_WM_CREATE()
	ON_WM_DESTROY()
	ON_WM_SIZE()
	ON_WM_LBUTTONDOWN()
	ON_WM_LBUTTONUP()
	ON_WM_RBUTTONDBLCLK()
	ON_WM_TIMER()
	ON_WM_MOUSEMOVE()
	ON_WM_MBUTTONDOWN()
	ON_WM_MBUTTONUP()
	ON_WM_MOUSEWHEEL()
	ON_WM_RBUTTONDOWN()
	ON_WM_RBUTTONUP()
	ON_WM_HSCROLL()
	ON_WM_VSCROLL()
	ON_WM_KEYDOWN()
END_MESSAGE_MAP()

// CTimelineView construction/destruction

CTimelineView::CTimelineView()
: m_pDoc(NULL),
  m_Height(0),
  m_Width(0),
  m_minX(0),
  m_ScrollX(0),
  m_ScrollY(0),
  m_bFlagAddingStatus(false),
  m_bInitialized(false),
  m_pPreSelecingFlag(NULL),
  m_pTimeline(NULL)
{
}

CTimelineView::~CTimelineView()
{
	m_oFlagMap.DeleteAll();

	for (UILines::iterator it = m_oUILines.begin(); it != m_oUILines.end(); it ++)
	{
		delete *it;
		*it = NULL;
	}
	m_oUILines.clear();
}

BOOL CTimelineView::PreCreateWindow(CREATESTRUCT& cs)
{
	return CScrollView::PreCreateWindow(cs);
}

int CTimelineView::OnCreate(LPCREATESTRUCT lpCreateStruct) 
{
	if (CScrollView::OnCreate(lpCreateStruct) == -1)
		return -1;
	
	// TODO: Add your specialized creation code here

	return 0;
}

void CTimelineView::OnInitialUpdate()
{
	CScrollView::OnInitialUpdate();

	CSize sizeTotal;
	sizeTotal.cx = m_Width;
	sizeTotal.cy = m_Height;
	SetScrollSizes(MM_TEXT, sizeTotal);

	m_bInitialized = true;
}

void CTimelineView::OnDraw(CDC* pDC)
{
	if (m_pDoc != NULL)
	{
		Draw(pDC);
	}
}

void CTimelineView::OnSize(UINT nType, int cx, int cy)
{
	CScrollView::OnSize(nType, cx, cy);

	CRect winRect;
	GetClientRect(&winRect);
	m_oSelectingRect.SetMaxRect(winRect);

	if (m_bInitialized)
	{
		if (m_oUILines.size() == 0)
		{
			InitializeGraphicsEntities(5, 100);
			m_bInitialized = false; // reset
		}
	}

	if (m_oUILines.size() != 0)
	{
		CPoint scrollPt = GetScrollPosition();
		m_ScrollX = scrollPt.x;
		m_ScrollY = scrollPt.y;
		m_oSelectingRect.SetScroll(m_ScrollX);
		m_oGraduation.SetScroll(m_ScrollX);
	}
}

void CTimelineView::OnDestroy()
{
	CScrollView::OnDestroy();
}

void CTimelineView::OnLButtonDown(UINT nFlags, CPoint point)
{
	if(m_oSelectingRect.IsExisted())
	{
		if (m_oSelectingRect.GetStatus() == CSelectingRect::ePreSelecting)
		{
			m_oSelectedPt = point;
			m_oSelectingRect.SetStatus(CSelectingRect::eSelecting);
		}
	}

	if (m_oSelectingRect.IsExisted())
	{
		if (m_oSelectingRect.GetStatus() == CSelectingRect::ePreSelecting
			|| m_oSelectingRect.GetStatus() == CSelectingRect::eSelecting)
		{
			return;
		}
	}

	if (m_bFlagAddingStatus)
	{
		CFlag* pFlag = new CFlag();
		POINT position = point;
		position.x = position.x + 2*m_ScrollX;
		position.y = position.y + m_ScrollY;
		position.x = m_oGraduation.OnSelect(position.x);
		position.y = AdjustYAsLine(position.y);
		pFlag->SetPosition(position);
		m_oFlagMap.Add(pFlag);

		InvalidateRect(NULL, false);

		CString strRowColumn;
		int row = GetRowIndex(position.y);
		int column = GetColumnIndex(position.x);
		strRowColumn.Format(_T("%d Row and %d Column"), row, column);
		AfxMessageBox(strRowColumn);
	}

	if (m_pPreSelecingFlag != NULL)
	{
		m_pPreSelecingFlag->SetStatus(CFlag::eSelecting);
		m_pPreSelecingFlag = NULL;
		InvalidateRect(NULL, false);
	}
}

int CTimelineView::AdjustYAsLine(int sourceY)
{
	int y2 = TOP_SPACE + GF_Y_SPACE + ROW_HEIGHT;

	int delta = sourceY - y2;
	int iNum = (int)(delta / ROW_HEIGHT);
	int y3 = (iNum) * ROW_HEIGHT + y2;
	int maxNum = (int)(m_oUILines.size());

	int y4 = y3 + ROW_HEIGHT;

	if (abs(y3 - sourceY) < abs(y4 - sourceY))
	{
		if (iNum >= maxNum)
			return (maxNum-1) * ROW_HEIGHT + y2;

		if (iNum <= 0)
			return (0) * ROW_HEIGHT + y2;

		return y3;
	}
	else
	{
		if (iNum + 1 >= maxNum)
			return (maxNum-1) * ROW_HEIGHT + y2;

		if (iNum + 1 <= 0)
			return (0) * ROW_HEIGHT + y2;

		return y4;
	}
}

void CTimelineView::OnLButtonUp(UINT nFlags, CPoint point)
{
	if(m_oSelectingRect.IsExisted())
	{
		if (m_oSelectingRect.GetStatus() == CSelectingRect::eSelecting || m_oSelectingRect.GetStatus() == CSelectingRect::ePreSelecting)
		{
			m_oSelectingRect.SetStatus(CSelectingRect::eNone);

			int targetX = m_oGraduation.OnSelect(2*m_ScrollX + point.x);
			m_oSelectingRect.MoveTo(targetX);
			InvalidateRect(NULL, true);
		}
	}
}

void CTimelineView::OnRButtonDblClk(UINT nFlags, CPoint point)
{
}

void CTimelineView::OnTimer(UINT_PTR nIDEvent)
{
}

void CTimelineView::UpdateView()
{
	InvalidateRect(NULL, false);
	OnDraw(GetDC());
}

void CTimelineView::OnMouseMove(UINT nFlags, CPoint point)
{
	CPoint scrollPt = GetScrollPosition();
	if (scrollPt.y != 0) return;

	if(m_oSelectingRect.IsExisted())
	{
		CSelectingRect::Status eStatus = m_oSelectingRect.GetStatus();
		switch (eStatus)
		{
		case CSelectingRect::eNone:
			{
				POINT WPt = point;
				WPt.x = UToW(WPt.x);

				if (m_oSelectingRect.IsSelecting(point))
					m_oSelectingRect.SetStatus(CSelectingRect::ePreSelecting);

				CRect rect = m_oSelectingRect.GetRect();
				rect.SetRect(WToU(rect.left), rect.top, WToU(rect.right), rect.bottom);
				InvalidateRect(rect, true);
			}
			break;

		case CSelectingRect::ePreSelecting:
			{
				if (!m_oSelectingRect.IsSelecting(point))
					m_oSelectingRect.SetStatus(CSelectingRect::eNone);

				CRect rect = m_oSelectingRect.GetRect();
				rect.SetRect(WToU(rect.left), rect.top, WToU(rect.right), rect.bottom);
				InvalidateRect(rect, true);
			}
			break;

		case CSelectingRect::eSelecting:
			{
				if (point.x < m_minX)
				{
					point.x = m_minX;
				}

				int iDeltaX = point.x - m_oSelectedPt.x;
				m_oSelectedPt = point;
				POINT cenPt = m_oSelectingRect.GetCenter();
				int iMinX = cenPt.x - GF_Y_SPACE;
				int iMaxX = cenPt.x + GF_Y_SPACE;
				m_oSelectingRect.Move(iDeltaX);
				POINT cenPt2 = m_oSelectingRect.GetCenter();
				cenPt2.x - GF_Y_SPACE < iMinX ? iMinX = cenPt2.x - GF_Y_SPACE : iMinX = iMinX;
				cenPt2.x + GF_Y_SPACE > iMaxX ? iMaxX = cenPt2.x + GF_Y_SPACE : iMaxX + iMaxX;
				RefreshRect(WToU(iMinX) - m_ScrollX, WToU(iMaxX) - m_ScrollX);
			}
			break;
		}
	}

	POINT position = point;
	position.x = position.x + 2*m_ScrollX;
	position.y = position.y + m_ScrollY;
	position.x = m_oGraduation.OnSelect(position.x);
	position.y = AdjustYAsLine(position.y);
	CFlag* pFlag = m_oFlagMap.Find(position);
	if (m_pPreSelecingFlag != NULL)
	{
		if (pFlag == m_pPreSelecingFlag)
		{
		}
		else
		{
			m_pPreSelecingFlag->UnSelect();
			m_pPreSelecingFlag = NULL;
			InvalidateRect(NULL, true);
		}
	}
	else
	{
		if (pFlag != NULL)
		{
			if (pFlag->GetStatus() != CFlag::eSelecting)
			{
				m_pPreSelecingFlag = pFlag;
				CRect rect = m_pPreSelecingFlag->PreSelect();
				InvalidateRect(rect, true);
			}
		}
	}
}

void CTimelineView::OnMButtonDown(UINT nFlags, CPoint point)
{
}

void CTimelineView::OnMButtonUp(UINT nFlags, CPoint point)
{
}

BOOL CTimelineView::OnMouseWheel(UINT nFlags, short zDelta, CPoint pt)
{
	return TRUE;
}

void CTimelineView::OnRButtonDown(UINT nFlags, CPoint point)
{
}

void CTimelineView::OnRButtonUp(UINT nFlags, CPoint point)
{
}

void CTimelineView::InitializeGraphicsEntities(long row, long frames)
{
	CRect winRect;
	GetClientRect(&winRect);

	POINT pt;
	pt.x = LEFT_SPACE;
	pt.y = TOP_SPACE;

	// init graduation
	m_oGraduation.SetFrameCount(frames);
	m_oGraduation.SetStartPt(pt);

	// init selecting rect
	m_oSelectingRect.SetRect(pt.x - 0.5*GF_Y_SPACE + 1, pt.y + GF_Y_SPACE / 2, pt.x + 0.5*GF_Y_SPACE + 1, pt.y + 1.5*GF_Y_SPACE);

	// init UI lines
	pt.y += GF_Y_SPACE;
	long lLength = frames * FRAME_X_SPACE;
	POINT ptNext;
	ptNext.x = pt.x + lLength;
	ptNext.y = pt.y;
	for (long lIndex  = 1; lIndex <= row; lIndex ++)
	{
		CUILine* pLine = new CUILine();
		m_oUILines.push_back(pLine);

		pt.y += ROW_HEIGHT;
		ptNext.y = pt.y;

		pLine->SetStartPt(pt);
		pLine->SetEndPt(ptNext);
	}

	// init some other parameters
	m_minX = winRect.left + LEFT_SPACE;
	m_Width = winRect.Width() - LEFT_SPACE;
	m_Height = pt.y + ROW_HEIGHT;
	OnInitialUpdate();
}

void CTimelineView::Draw(CDC* pDC)
{
	// draw graduation
	DrawGraduation(pDC);

	// record selecting rect position
	DrawSelectingRect(pDC);

	// draw lines for families
	DrawLines(pDC);

	// draw flags
	DrawFlags(pDC);
}

void CTimelineView::DrawGraduation(CDC* pDC)
{
	m_oGraduation.Draw(pDC);
}

void CTimelineView::DrawSelectingRect(CDC* pDC)
{
	m_oSelectingRect.IsExisted();
	m_oSelectingRect.Draw(pDC);
}

void CTimelineView::DrawLines(CDC* pDC)
{
	for (UILines::iterator it = m_oUILines.begin(); it != m_oUILines.end(); it ++)
	{
		(*it)->Draw(pDC);
	}
}

void CTimelineView::DrawFlags(CDC* pDC)
{
	m_oFlagMap.SetScroll(m_ScrollX);
	m_oFlagMap.DrawAll(pDC);
}

void CTimelineView::RefreshRect(int iMinX, int iMaxX)
{
	CRect winRect;
	GetClientRect(&winRect);
	
	CRect rect;
	rect.SetRect(iMinX, winRect.top, iMaxX, winRect.bottom);
	InvalidateRect(rect, true);
}

void CTimelineView::OnHScroll(UINT nSBCode, UINT nPos, CScrollBar* pScrollBar)
{
	__super::OnHScroll(nSBCode, nPos, pScrollBar);

	CPoint scrollPt = GetScrollPosition();
	m_ScrollX = scrollPt.x;

	m_oSelectingRect.SetScroll(m_ScrollX);
	m_oGraduation.SetScroll(m_ScrollX);

	InvalidateRect(NULL, true);
}

void CTimelineView::OnVScroll(UINT nSBCode, UINT nPos, CScrollBar* pScrollBar)
{
	__super::OnVScroll(nSBCode, nPos, pScrollBar);

	CPoint scrollPt = GetScrollPosition();
	m_ScrollY = scrollPt.y;

	InvalidateRect(NULL, true);
}

int CTimelineView::UToW(int x)
{
	return x + m_ScrollX;
}

int CTimelineView::WToU(int x)
{
	return x - m_ScrollX;
}

void CTimelineView::OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags)
{
	switch (nChar)
	{
	case VK_DELETE:
		m_oFlagMap.DeleteAllSelection();
		InvalidateRect(NULL, true);
		break;
	}
}

void CTimelineView::CreateKnot(int row, int column)
{
	if (row < 0 || column < 0)
		return;

	Families* pFamilies = m_pTimeline->GetFamilies();
	CFamily* pFamily = (*pFamilies)[row];
	


}

int CTimelineView::GetRowIndex(long y)
{
	for (int i = 0; i < (int)m_oUILines.size(); i ++)
	{
		CUILine* pUILine = m_oUILines[i];
		if (i == 0)
		{
			if (pUILine->GetHeight() >= y)
			{
				return i;
			}
		}
		else
		{
			if (m_oUILines[i-1]->GetHeight() < y && m_oUILines[i]->GetHeight() >= y)
			{
				return i;
			}
		}
	}

	return -1;
}

int CTimelineView::GetColumnIndex(long x)
{
	return m_oGraduation.GetIndex(x);
}